package com.unione.heathrobot

import android.text.TextUtils
import android.util.Log
import androidx.lifecycle.viewModelScope
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.ToastUtils
import com.google.gson.Gson
import com.unione.heathrobot.constant.AmpParams
import com.unione.heathrobot.constant.AppConstant.SP_KEY_CHARGE_BATTERY
import com.unione.heathrobot.constant.AppConstant.SP_KEY_LOWBATTERY
import com.unione.heathrobot.constant.AppConstant.default_charge_battery
import com.unione.heathrobot.constant.AppConstant.default_lowbattery
import com.unione.unione_network.IReceiveMessage
import com.unione.unione_network.WebSocketManager
import com.unione.unione_network.request.UnioneRobotRepository
import com.unione.unione_network.robot_auto.bean.RobotInfo
import com.unione.unione_network.robot_auto.bean.RoutePoint
import com.unione.unione_network.robot_auto.request.MoveToPointRequest
import com.unione.unione_voice.engine.EngineConstants
import com.unione.unione_voice.i2c.I2CManager
import com.unione.unione_voice.utils.SPUtil
import com.unione.unione_voice.utils.SoundPoolUtil
import com.unione.unione_voice.viewmodel.CommonAIVoiceModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import okhttp3.RequestBody.Companion.toRequestBody
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
import kotlin.math.pow
import kotlin.math.sqrt

class MainViewModel : CommonAIVoiceModel(), IReceiveMessage {

    private lateinit var webSocketManager: WebSocketManager
    private var blockCount = 0
    private var playMusicService = Executors.newScheduledThreadPool(1)
    private var patrolService = Executors.newScheduledThreadPool(1)
    private val taskMap: ConcurrentMap<String, ScheduledFuture<*>> = ConcurrentHashMap()
    private var mIsFirstGetMessage = true

    @Volatile
    private var mRobotInfo: RobotInfo? = null

    @Volatile
    private var mMapRoutePointList: MutableList<RoutePoint> = mutableListOf()
    private var currentAutoStatus: String = ""
    private var mCurrentScene = ""

    private var mGetLowBatterySignal: Boolean = false

    private var mDockSemaphore: Semaphore = Semaphore(1)


    // 任务锁
    private val taskLock = ReentrantLock()

    // 导航锁
    private val taskNavCondition = taskLock.newCondition()

    // 备份巡逻任务时间 分钟
    private var mPatrolTimeInterval = 1

    var mInitPointId = ""

    @Volatile
    private lateinit var mDstPoint: RoutePoint

    companion object {
        const val TAG = "MainViewModel"
        private const val KEY_PLAY_BLOCK_MUSIC = "key_play_block_music"
        private const val KEY_PATROL_TASK = "key_patrol_task"
    }

    /**
     *  初始化机器人websocket
     */
    fun initWebSocketManager() {
        webSocketManager = WebSocketManager.getInstance()
        webSocketManager.init(this)
    }

    override fun onConnectSuccess() {
        LogUtils.e("websocket onConnectSuccess")
    }

    override fun onConnectFailed() {
        LogUtils.e("websocket onConnectFailed")
    }

    override fun onClose() {
        LogUtils.e("websocket close")
        initWebSocketManager()
    }

    override fun onMessage(text: String?) {
        LogUtils.i("收到消息 $text ")
        if (!TextUtils.isEmpty(text)) {
            if (mIsFirstGetMessage || mMapRoutePointList.isEmpty()) {
                getRobotInfo()
            }
            try {
                mRobotInfo = Gson().fromJson(text, RobotInfo::class.java)
                if (mRobotInfo!!.autoStatus.toInt() == 4 || mRobotInfo!!.autoStatus.toInt() == 6) {
                    blockCount++
                } else {
                    blockCount = 0
                }
                if (blockCount > 3) playBlockMusic() else stopPlayMusic()
                if (mRobotInfo!!.chargingStatus == "1" || mRobotInfo!!.dockStatus == "2") {
                    if (mDockSemaphore.availablePermits() == 0) {
                        LogUtils.e("对装完成或者在充电中，当前信号量值为${mDockSemaphore.availablePermits()},准备释放")
                        mDockSemaphore.release()
                        navFinish()
                    }
                }
                if (currentAutoStatus != "" && currentAutoStatus.toInt() > 2 && mRobotInfo!!.autoStatus.toInt() == 2) {
                    LogUtils.i("备份状态$currentAutoStatus  当前状态${mRobotInfo!!.autoStatus}")
                    if (mDockSemaphore.availablePermits() == 1 && checkIsArrived()) {
                        navFinish()
                    }
                }
                if (mRobotInfo!!.powerPercentage.isNotEmpty() && mRobotInfo!!.powerPercentage.toInt() <= SPUtil.getInt(
                        HeathRobotApplication.getContext(),
                        SP_KEY_LOWBATTERY,
                        default_lowbattery
                    )
                ) {
                    LogUtils.e("电量低于" + default_lowbattery + "  " + mRobotInfo!!.powerPercentage)
                    mGetLowBatterySignal = true
                }
                if (mRobotInfo!!.powerPercentage.isNotEmpty() && mRobotInfo!!.powerPercentage.toInt() >= SPUtil.getInt(
                        HeathRobotApplication.getContext(), SP_KEY_CHARGE_BATTERY,
                        default_charge_battery
                    )&&mGetLowBatterySignal
                ) {
                    mGetLowBatterySignal = false
                    // 恢复之前任务
                    startPatrolTask(mPatrolTimeInterval)
                }
                if (mRobotInfo!!.powerPercentage.isNotEmpty() && mRobotInfo!!.powerPercentage.toInt() <= SPUtil.getInt(
                        HeathRobotApplication.getContext(),
                        SP_KEY_LOWBATTERY,
                        default_lowbattery
                    ) && mRobotInfo!!.chargingStatus != "1" && mRobotInfo!!.workMode != "2" && mRobotInfo!!.dockStatus != "2" &&
                    mRobotInfo!!.autoStatus.toInt() in 2..6
                ) {
                    stopPatrolTask()
                    viewModelScope.launch(Dispatchers.IO) {
                        startCharge()
                    }
                }

                LogUtils.i("机器AGV导航状态从 $currentAutoStatus 变为 ${mRobotInfo!!.autoStatus}")
                currentAutoStatus = mRobotInfo!!.autoStatus
            } catch (e: Exception) {
                e.localizedMessage?.let { LogUtils.d(TAG, it) }
            }
        }
    }

    private fun checkIsArrived(): Boolean {
        val distance = sqrt(
            (mRobotInfo!!.x.toDouble() - mDstPoint.x.toDouble()).pow(2) + (mRobotInfo!!.y.toDouble() - mDstPoint.y.toDouble()).pow(
                2
            )
        )
        val isArrived = distance <= 0.2
        LogUtils.e("计算到达目标点范围结果为 $distance $isArrived")
        return isArrived
    }


    /**
     *  开始充电
     */
    private fun startCharge(isLowBattery: Boolean = true) {
        LogUtils.e("当前信号量值为 ${mDockSemaphore.availablePermits()}")
        if (!mDockSemaphore.tryAcquire()) return
        LogUtils.e("准备自动充电   是否是低电量自动充电：$isLowBattery")
        move2Charge(maxRetries = 5)
        navWaiting()
    }


    private fun getRobotInfo() {
        viewModelScope.launch {
            try {
                val rsp = UnioneRobotRepository.getRobotInfo()
                if (rsp.isSuccess()) {
                    mRobotInfo = rsp.result
                    mIsFirstGetMessage = false
                    mRobotInfo?.let {
                        mCurrentScene = mRobotInfo!!.currentScene
                        getRobotMapRoadInfo(mRobotInfo!!.currentScene)
                    }
                }
            } catch (e: Exception) {
                LogUtils.e(e.localizedMessage)
            }
        }
    }

    /**
     *  获取路网点信息
     */
    fun getRobotMapRoadInfo(currentScene: String) {
        viewModelScope.launch {
            try {
                val rsp = UnioneRobotRepository.getMapRoadInfo(currentScene)
                if (rsp.isSuccess()) {
                    if (rsp.result.isNullOrEmpty()) return@launch
                    mMapRoutePointList.clear()
                    mMapRoutePointList.addAll(rsp.result!!)
                    mInitPointId = rsp.result!![0].id
                    if (mRobotInfo!!.autoStatus.toInt() == 0) {
                        startRobotAutoMode(mCurrentScene, mInitPointId)
                    }
                }

            } catch (e: Exception) {
                e.localizedMessage?.let { Log.e(TAG, it) }
            }

        }
    }

    /**
     *  机器人自主初始化
     */
    fun startRobotAutoMode(currentScene: String, id: String) {
        viewModelScope.launch {
            try {
                val rsp = UnioneRobotRepository.setAutoMode(1, currentScene, id)
                if (rsp.isSuccess() || rsp.code == 813) {

                } else {

                }
                ToastUtils.showLong(rsp.message)
            } catch (e: Exception) {
                Log.i(TAG, e.localizedMessage)
            }
        }
    }

    private fun stopPlayMusic() {
        playMusicService.shutdownNow()
        taskMap.remove(KEY_PLAY_BLOCK_MUSIC)
        EngineConstants.isEnableRecording = true
        EngineConstants.isPlayingTTS = false
    }

    private fun playBlockMusic() {
        if (taskMap.containsKey(KEY_PLAY_BLOCK_MUSIC)) {
            return
        }
        if (playMusicService.isTerminated || playMusicService.isShutdown || playMusicService == null) {
            playMusicService = Executors.newScheduledThreadPool(1)
        }
        val scheduledFuture =
            playMusicService.scheduleWithFixedDelay({
                try {
                    SoundPoolUtil.play("阻挡提示音")
                    EngineConstants.isEnableRecording = false
                    EngineConstants.isPlayingTTS = true
                } catch (e: Exception) {

                }
            }, 0, 10, TimeUnit.SECONDS)
        taskMap[KEY_PLAY_BLOCK_MUSIC] = scheduledFuture
    }

    /**
     *  开启巡逻任务
     *  @param time  间隔分钟
     */
    fun startPatrolTask(time: Int) {
        if (mMapRoutePointList.isEmpty() || mRobotInfo == null) {
            ToastUtils.showLong("机器还没有准备好")
            return
        }

        if (mRobotInfo?.chargingStatus == "1" && mGetLowBatterySignal) {
            ToastUtils.showLong("机器人充电中")
            return
        }
        if (!mRobotInfo?.powerPercentage.isNullOrEmpty() &&
            mRobotInfo?.powerPercentage!!.toInt() <= SPUtil.getInt(
                HeathRobotApplication.getContext(),
                SP_KEY_LOWBATTERY,
                default_lowbattery
            )
        ) {
            ToastUtils.showLong("机器人电量低")
            return
        }
        if (taskMap.containsKey(KEY_PATROL_TASK)) {
            stopPatrolTask()
        }
        if (patrolService.isTerminated || patrolService.isShutdown || patrolService == null) {
            patrolService = Executors.newScheduledThreadPool(1)
        }
        mPatrolTimeInterval = time
        val patrolTask = patrolService.scheduleWithFixedDelay({
            doPatrolTask()
        }, 0, mPatrolTimeInterval * 60L, TimeUnit.SECONDS)
        taskMap[KEY_PATROL_TASK] = patrolTask
    }

    /**
     *  具体的巡逻任务
     */
    private fun doPatrolTask() {
        for (point in mMapRoutePointList.withIndex()) {
            if (point.index == 0) continue
            mDstPoint = point.value
            LogUtils.e("去目标点位" + point.value.id)
            move2DstPoint(mDstPoint, maxRetries = 5)
            try {
                taskLock.lock()
                LogUtils.e("等待导航完成")
                taskNavCondition.await()
            } catch (e: InterruptedException) {
                e.printStackTrace()
                Thread.currentThread().interrupt()
                break
            } finally {
                taskLock.unlock()
            }
            LogUtils.e("导航完成，准备去下一个点")
        }
        if (!Thread.currentThread().isInterrupted) {
            startCharge(false)
        }
    }

    /**
     *  下发去目标点
     */
    private fun move2DstPoint(point: RoutePoint, maxRetries: Int, retryCount: Int = 0) {
        viewModelScope.launch {
            try {
                val requestBody =
                    Gson().toJson(
                        MoveToPointRequest(
                            point.id,
                            mCurrentScene,
                            point.yaw
                        )
                    ).toRequestBody()
                val rsp = UnioneRobotRepository.moveToDstPoint(requestBody)
                if (!rsp.isSuccess()) {
                    if (retryCount < maxRetries) {
                        LogUtils.e("下发去目标点${point.id}命令失败，重试次数：$retryCount/$maxRetries，继续发送")
                        move2DstPoint(point, maxRetries, retryCount + 1)
                    } else {
                        LogUtils.e("下发去目标点${point.id}命令失败，已达到最大重试次数：$maxRetries，不再重试")
                    }
                } else {
                    LogUtils.e("下发去目标点${point.id}命令成功")
                }
            } catch (e: Exception) {
                if (retryCount < maxRetries) {
                    LogUtils.e("异步请求出错，重试次数：$retryCount/$maxRetries，继续发送")
                    move2DstPoint(point, maxRetries, retryCount + 1)
                } else {
                    LogUtils.e("异步请求出错，已达到最大重试次数：$maxRetries，不再重试")
                }
            }
        }
    }

    /**
     *  自动充电
     */
    private fun move2Charge(maxRetries: Int, retryCount: Int = 0) {
        viewModelScope.launch {
            try {
                val rsp = UnioneRobotRepository.oneKeyCharge()
                if (rsp.isSuccess()) {
                    LogUtils.e("发送充电命令成功" + rsp.message)
                } else {
                    if (retryCount < maxRetries) {
                        LogUtils.e("发送充电命令失败，重试次数：$retryCount/$maxRetries，继续发送")
                        move2Charge(maxRetries, retryCount + 1)
                    } else {
                        LogUtils.e("发送充电命令失败，已达到最大重试次数：$maxRetries，不再重试")
                    }
                }
            } catch (e: Exception) {
                if (retryCount < maxRetries) {
                    LogUtils.e("发送充电异步请求出错，重试次数：$retryCount/$maxRetries，继续发送 ${e.localizedMessage}")
                    move2Charge(maxRetries, retryCount + 1)
                } else {
                    LogUtils.e("发送充电异步请求出错，已达到最大重试次数：$maxRetries，不再重试 ${e.localizedMessage}")
                }
            }
        }
    }

    /**
     *  停止巡逻任务
     */
    private fun stopPatrolTask() {
        LogUtils.e("取消了循环任务")
        taskMap[KEY_PATROL_TASK]?.cancel(true)
        patrolService.shutdownNow()
        taskMap.remove(KEY_PATROL_TASK)
    }

    private fun navWaiting() {
        try {
            taskLock.lock()
            LogUtils.e("等待导航完成")
            taskNavCondition.await()
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            taskLock.unlock()
        }
    }

    private fun navFinish() {
        try {
            taskLock.lock()
            // 唤醒锁
            taskNavCondition.signal()
            LogUtils.e("导航完成")
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            taskLock.unlock()
        }
    }


    fun initI2C() {
//        RootShell.execRootCmdSilent("chmod 777 /dev/i2c-0")
//        RootShell.execRootCmdSilent("chmod 777 /dev/i2c-1")
//        RootShell.execRootCmdSilent("chmod 777 /dev/i2c-2")
//        RootShell.execRootCmdSilent("chmod 777 /dev/i2c-3")
//        RootShell.execRootCmdSilent("chmod 777 /dev/i2c-4")
//        RootShell.execRootCmdSilent("chmod 777 /dev/i2c-5")
//        RootShell.execRootCmdSilent("chmod 777 /dev/i2c-6")
        viewModelScope.launch(Dispatchers.IO) {
            try {
                // AmpParams.init()
                val i2cManager: I2CManager = I2CManager(AmpParams.i2c_bus_num)
                val isOpen = i2cManager.open()
                if (isOpen) {
                    for (entry in AmpParams.Reg) {
                        val byteArray = byteArrayOf(entry.key.toByte(), entry.value.toByte())
                        i2cManager.write(AmpParams.PA_Addr.toByte(), byteArray)
                        Log.i("native-i2c-log", "写数据" + entry.key.toString())
                    }
                    i2cManager.close()
                }
            } catch (e: Exception) {
                Log.e(TAG, e.localizedMessage)
            }

        }

    }


    override fun onDestroy() {
        super.onDestroy()
        webSocketManager.close()
    }

}